/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2019.
 * Description: softdog driver
 * Author: xiaowei
 * Create: 2019-07-18
 */
#include <linux/of.h>
#include <linux/kthread.h>
#include <uapi/linux/sched/types.h>
#include <linux/semaphore.h>
#include <linux/hal/watchdog.h>

#define SOFTDOG_INTERVA		(10 * HZ)
static long interval = SOFTDOG_INTERVA;

static struct task_struct *softdog_tsk;
static struct semaphore softdog_sem;

static int softdog(void *data)
{
	struct sched_param param = {
		.sched_priority = DEFAULT_PRIO
	};

	sched_setscheduler(current, SCHED_NORMAL, &param);
	set_current_state(TASK_INTERRUPTIBLE);
	while (!kthread_should_stop()) {
		hwdog_set_feed_count(0);
		schedule_timeout(interval);
		if (kthread_should_stop())
			break;
		set_current_state(TASK_INTERRUPTIBLE);
	}
	__set_current_state(TASK_RUNNING);

	return 0;
}

void softdog_stop(void)
{
	if (softdog_tsk) {
		kthread_stop(softdog_tsk);
		softdog_tsk = NULL;
	}
}

void softdog_set_interval(unsigned int sec)
{
	interval = sec * HZ;
}

int softdog_kthread_create(void)
{
	int ret;

	pr_info("%s\n", __func__);

	ret = down_interruptible(&softdog_sem);
	if (ret < 0)
		return ret; /* -EINTR */

	if (softdog_tsk) {
		pr_err("softdog tsk is exist\n");
		ret = -EEXIST;
		goto out;
	}

	softdog_tsk = kthread_create(softdog, NULL, "swtd");
	if (IS_ERR(softdog_tsk)) {
		ret = PTR_ERR(softdog_tsk);
		softdog_tsk = NULL;
		pr_err(DTS_WDT_WARN "Create softdog failed\n");
		goto out;
	}
	wake_up_process(softdog_tsk);

out:
	up(&softdog_sem);
	return ret;
}
EXPORT_SYMBOL(softdog_kthread_create);

void softdog_init(void)
{
	int enable = 0;
	struct device_node *node = NULL;

	sema_init(&softdog_sem, 1);

	node = of_find_compatible_node(NULL, NULL, "rtos-wtd-config");
	if (!node)
		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-wdt");
	if (!node)
		node = of_find_compatible_node(NULL, NULL, "arm,arm64-wdt");
	if (!node)
		return;

	if (of_property_read_u32(node, "softdog-enable", &enable))
		of_property_read_u32(node, "senior-dog-enable", &enable);

	if (enable)
		(void)softdog_kthread_create();
}

void rtos_wdt_stop(void)
{
	hwdog_stop();
	softdog_stop();
	pr_info("HWDOG and SOFTDOG stopped successfully\n");
}
EXPORT_SYMBOL(rtos_wdt_stop);
